package com.hmall.search.service.impl;

import cn.hutool.core.util.StrUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.elasticsearch.core.search.TotalHits;
import co.elastic.clients.json.JsonData;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hmall.common.domain.PageDTO;
import com.hmall.search.domain.po.ItemDoc;
import com.hmall.search.domain.query.ItemPageQuery;
import com.hmall.search.service.ISearchService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author zzw
 * @date 2025/6/25
 * @description 搜索服务实现类
 */
@Slf4j
@Service
@AllArgsConstructor
public class SearchServiceImpl implements ISearchService {

    private final ElasticsearchClient esClient;

    @Override
    public PageDTO<ItemDoc> search(ItemPageQuery query) {

        try {
            SearchRequest.Builder builder = new SearchRequest.Builder();

            // 构建查询条件
            builder.query(q -> {
                       q.bool(b -> {
                           b.must(m -> {
                               // 如果搜索关键字不为空
                               if (StrUtil.isNotEmpty(query.getKey())) {
                                   m.multiMatch(mm -> mm
                                           .query(query.getKey())
                                           .fields("name", "brand", "category", "spec"));
                               } else {
                                   m.matchAll(ma -> ma);
                               }
                               return m;
                           });

                           // 如果分类不为空
                           if (StrUtil.isNotEmpty(query.getCategory())) {
                               b.filter(f -> f.term(t -> t.field("category").value(query.getCategory())));
                           }

                           // 如果品牌不为空
                           if (StrUtil.isNotEmpty(query.getBrand())) {
                               b.filter(f -> f.term(t -> t.field("brand").value(query.getBrand())));
                           }

                           // 如果价格区间不为空
                           if (query.getMinPrice() != null && query.getMaxPrice() != null) {
                               b.filter(f -> f.range(r -> r.field("price").lte(JsonData.of(query.getMaxPrice()))
                                                           .gte(JsonData.of(query.getMinPrice()))));
                           }

                           return b;
                       });

                       return q;
                   })
                   .size(query.getPageSize())
                   .from(query.from())
            ;

            // 指定索引
            builder.index("items");

            // 排序
            if (StrUtil.isNotEmpty(query.getSortBy())) {
                builder.sort(s -> s.field(f -> f.field(query.getSortBy()).order(query.getIsAsc() ? SortOrder.Asc :
                                                                                        SortOrder.Desc)));
            }

            // 高亮
            builder.highlight(h -> h
                                      .fields(
                                              "name", f -> f
                                                      .preTags("<em>")
                                                      .postTags("</em>")
                                             )
                             );

            // 构建请求
            SearchRequest build = builder.build();

            // 发送请求
            SearchResponse<ItemDoc> response = esClient.search(build, ItemDoc.class);

            // 解析响应
            // 1.获取总条数
            TotalHits total = response.hits().total();
            // 2.获取结果列表
            List<Hit<ItemDoc>> hits = response.hits().hits();
            // 3.转换为List<ItemDoc>
            List<ItemDoc> itemDocs = hits.stream().map(hit -> {
                ItemDoc source = hit.source();
                // 高亮处理
                Map<String, List<String>> highlightFields = hit.highlight();
                if (highlightFields != null && !highlightFields.isEmpty()) {
                    List<String> name = highlightFields.get("name");
                    if (name != null && !name.isEmpty()) {
                        if (source != null) {
                            source.setName(name.get(0));
                        }
                    }
                }
                return source;
            }).collect(Collectors.toList());

            // 4.封装结果
            if (total != null) {
                Page<ItemDoc> page = Page.of(query.getPageNo(), query.getPageSize(), total.value());
                PageDTO<ItemDoc> pageDTO = PageDTO.of(page);
                pageDTO.setList(itemDocs);
                return pageDTO;
            }
        }
        catch (IOException e) {
            log.error("搜索异常", e);
        }

        return null;
    }

    @Override
    public List<String> searchSuggestion(String key) {

        if (StrUtil.isEmpty(key)) {
            return List.of();
        }

        log.info("搜索补全:{}", key);

        List<String> suggestions = new ArrayList<>();

        // 1.构建请求
        SearchRequest.Builder builder = new SearchRequest.Builder();
        builder.index("items");
        builder.suggest(s -> s
                                .suggesters(
                                        "suggestion_suggest", ss -> ss
                                                .completion(c -> c
                                                        .field("suggestion")
                                                        .size(200)
                                                        .skipDuplicates(true))
                                                .text(key)
                                           )
                       );

        SearchRequest request = builder.build();

        try {
            // 2.发送请求
            SearchResponse<ItemDoc> response = esClient.search(request, ItemDoc.class);

            // 3.解析响应
            // TODO 无法使输入中文时优先输出中文的匹配内容
            Map<String, List<Suggestion<ItemDoc>>> suggest = response.suggest();
            List<Suggestion<ItemDoc>> suggestionSuggest = suggest.get("suggestion_suggest");
            suggestionSuggest.forEach(s -> {
                s.completion().options().forEach(option -> {
                    suggestions.add(option.text());
                });
            });

            // 4.返回结果
            log.info("搜索补全结果:{}", suggestions);
            return suggestions;
        }
        catch (IOException e) {
            log.error("搜索补全异常", e);
        }
        return List.of();
    }
}
